﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Input;
using System.Drawing;
using OpenTK.Graphics;
using Common;

namespace T14_RenderToTexture {
    class T14_RenderToTextureGameWindow : GameWindow {
        public T14_RenderToTextureGameWindow() {
            this.Load += T14_RenderToTextureGameWindow_Load;
            this.Resize += T14_RenderToTextureGameWindow_Resize;
            this.RenderFrame += T14_RenderToTextureGameWindow_RenderFrame;
            this.Unload += T14_RenderToTextureGameWindow_Unload;
            this.Width = 1024;
            this.Height = 768;
            this.Title = "T14_RenderToTextureGameWindow";

        }


        private void T14_RenderToTextureGameWindow_RenderFrame(object sender, FrameEventArgs e) {
            //GL.ClearColor(.1f, .1f, .99f, .9f);
            //GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            Render();

            SwapBuffers();
            ProcessEvents();
        }

        Shader shader;
        Shader quadShader;
        Vao vao;
        Texture2D texture;
        ModelMesh mesh;
        Vbo<Vector3> vertexBuffer, normalBuffer;
        Vbo<Vector2> uvBuffer;
        Vbo<ushort> indexBuffer;
        VertexAttribManager attribs = new VertexAttribManager();
        int frameBufferName = 0;
        int renderedTexture;
        int depthRenderBuffer;
        float[] quadVertexBufferData={
                    -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
        -1.0f,  1.0f, 0.0f,
        -1.0f,  1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         1.0f,  1.0f, 0.0f,
        };
        int texID;
        int timeID;
        Vbo<float> quadBuffer;
        VertexAttribManager quadAttribs = new VertexAttribManager();
        private void T14_RenderToTextureGameWindow_Load(object sender, EventArgs e) {
            GL.ClearColor(0, 0, .4f, 0);
            VSync = VSyncMode.Off;
            GL.Enable(EnableCap.DepthTest);
            GL.DepthFunc(DepthFunction.Less);
            GL.Enable(EnableCap.CullFace);

            vao = new Vao();
            shader = new Shader("shader.vs", "shader.fs");
            quadShader = new Shader("Passthrough.vs", "WobblyTexture.fs");

            texture = Texture2D.Load("uvmap.dds");
            mesh = MeshLoader.LoadObjIndexed("suzanne.obj");

            vertexBuffer = new Vbo<Vector3>(mesh.Vertices);
            uvBuffer = new Vbo<Vector2>(mesh.Uvs);
            normalBuffer = new Vbo<Vector3>(mesh.Normals);
            indexBuffer = new Vbo<ushort>(BufferTarget.ElementArrayBuffer, BufferUsageHint.StaticDraw, mesh.Indices);

            attribs.AddAttrib(3, vertexBuffer);
            attribs.AddAttrib(2, uvBuffer);
            attribs.AddAttrib(3, normalBuffer);

            frameBufferName = GL.GenFramebuffer();
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, frameBufferName);

            renderedTexture = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, renderedTexture);

            //Give an empty image to OpenGL(last "0" means "empty");
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb, this.Width, this.Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);

            depthRenderBuffer = GL.GenRenderbuffer();
            GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthRenderBuffer);
            GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent, this.Width, this.Height);
            GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, RenderbufferTarget.Renderbuffer, depthRenderBuffer);

            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, renderedTexture, 0);

            GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
            if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete) {
                throw new Exception("framwbuffer error");
            }


            quadBuffer = new Vbo<float>(quadVertexBufferData);
            quadAttribs.AddAttrib(3, quadBuffer);

            texID = quadShader.GetUniformLocation("renderedTexture");
            timeID = quadShader.GetUniformLocation("time");



        }

        private void Render() {
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, frameBufferName);
            //GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
            GL.Viewport(0, 0, this.Width, this.Height);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            shader.Use();

            InputControls.computeMatricesFromInput(this);

            var projection = InputControls.ProjectionMatrix;
            var view = InputControls.ViewMatrix;
            var model = Matrix4.Identity;
            var mvp = model * view * projection;

            shader.SetMatrix4("MVP", ref mvp);
            shader.SetMatrix4("M", ref model);
            shader.SetMatrix4("V", ref view);

            Vector3 lightPos = new Vector3(4, 4, 4);
            shader.SetVector3("LightPosition_worldspace", lightPos);

            texture.Active(TextureUnit.Texture0, shader,"myTextureSampler");

            attribs.Enable();
            GL.DrawElements(BeginMode.Triangles, mesh.Indices.Length, DrawElementsType.UnsignedShort, 0);
            attribs.Disable();

            //render to the screen
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
            GL.Viewport(0, 0, this.Width, this.Height);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            quadShader.Use();

            GL.ActiveTexture(TextureUnit.Texture0);
            GL.BindTexture(TextureTarget.Texture2D, renderedTexture);
            quadShader.SetInt("renderedTexture", 0);
            quadShader.SetFloat("time", (float)InputControls.getTime() * 10f);

            quadAttribs.Enable();
            GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
            quadAttribs.Disable();

        }

        private void T14_RenderToTextureGameWindow_Resize(object sender, EventArgs e) {
            GL.Viewport(0, 0, Width, Height);
        }



        private void T14_RenderToTextureGameWindow_Unload(object sender, EventArgs e) {
            GlDisposableObjectBase.DisposeObjects(shader, quadShader,vao,texture,vertexBuffer,uvBuffer,normalBuffer,indexBuffer,quadBuffer,mesh);
        }

    }
}
